﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FDSimpleModelGenerator.Common;
using FDSimpleModelGenerator.Common.Enums;
using FDSimpleModelGenerator.Entities;
using FDSimpleModelGenerator.Entities.Enums;
using FDSimpleModelGenerator.IServices;
using Npgsql;

namespace FDSimpleModelGenerator.Services.PostgreSQL
{
    /// <summary>
    /// 生成PostgreSQL模型
    /// 关键注意点：PostgreSQL区分大小写，表名一定要一模一样
    /// </summary>
    public class GenerateModelPostgreSqlService : IGenerateModelService
    {
        private readonly IExceptionThrowFactory _exceptionThrowFactory;
        private readonly IDbConfigResolver _configResolver;
        private readonly IDbAccessor _dbAccessor;
        private readonly IModelGenerateEngine _modelGenerateEngine;

        public GenerateModelPostgreSqlService(IExceptionThrowFactory exceptionThrowFactory, IDbAccessor dbAccessor,
            IModelGenerateEngine modelGenerateEngine, IEnumerable<IDbConfigResolver> dbConfigResolvers)
        {
            _modelGenerateEngine = modelGenerateEngine;
            _dbAccessor = dbAccessor;
            _exceptionThrowFactory = exceptionThrowFactory;
            _configResolver = dbConfigResolvers.First(r => r.DbType == DbType);
        }

        public DbType DbType => DbType.PostgreSQL;

        public async Task<string> GenerateAsync(DbConfig dbConfig, string tableName, string templateContent,
            string namespaceName = "FDSimpleModelGenerator", LanguageDataType languageDataType = LanguageDataType.CSharp, bool propertyFirstCharLower = false)
        {
            return await Task.Run(() =>
                Generate(dbConfig, tableName, templateContent, namespaceName, languageDataType, propertyFirstCharLower));
        }

        /// <summary>
        /// 生成模型
        /// </summary>
        /// <param name="dbConfig"></param>
        /// <param name="tableName"></param>
        /// <param name="templateContent"></param>
        /// <param name="namespaceName">命名空间</param>
        /// <param name="propertyFirstCharLower"></param>
        /// <param name="languageDataType"></param>
        /// <returns></returns>
        public string Generate(DbConfig dbConfig, string tableName, string templateContent, string namespaceName, LanguageDataType languageDataType, bool propertyFirstCharLower)
        {
            var connString = _configResolver.GetConnectionString(dbConfig);
            _dbAccessor.Init(() => new NpgsqlConnection(connString));
            var sql = @"select a.attname as name,t.typname as ColumnType,d.description,case when cc.contype='p' then 1 else 0 end as Key,a.atttypmod as Length
                    from pg_class as c
                    inner join pg_attribute as a on a.attrelid = c.oid
                    inner join pg_type as t on t.oid = a.atttypid
                    left join pg_constraint as cc on cc.conrelid = c.oid and  a.attnum = cc.conkey[1]
                    left join pg_description d on a.attrelid=d.objoid and a.attnum=d.objsubid
                    where c.relname=@tableName and a.attnum>0";
            var sqlTable = @"select relname as name,
                cast(obj_description(relfilenode,'pg_class') as varchar) as value from pg_class c 
                where relname =@tableName;";

            var tableInfo = _dbAccessor.Get<NameValue<string>>(sqlTable, new { tableName, dbName = dbConfig.Database });
            var columns = _dbAccessor.GetList<TableColumn>(sql, new { tableName, dbName = dbConfig.Database });

            var tableParam = new TableInfo
            {
                Description = tableInfo.Value,
                Name = tableInfo.Name,
                TableColumns = columns
            };
            return _modelGenerateEngine.Generate(tableParam, templateContent, namespaceName, languageDataType, propertyFirstCharLower);
        }
    }
}
